package exception;

/* 异常就是代表程序出现问题
   学习异常的目的就是为了处理异常


                      Error
java.lang.Throwable
                      Exception    RuntimeException
                                   其他异常

Error
代表系统级别的错误(属于严重问题)
这个给sun公司用的不是给程序员用的，开发人员不需要管

Exception
叫异常类，代表程序出现的问题 我们通常用他的子类来封装程序出现的问题
所以我们需要学习的是Exception子类就行了

运行时异常
RuntimeException及子类在编译阶段不会报错，在运行阶段才会报错

编译时异常
编译阶段就会出现异常提醒

异常的意义
1.用来查询bug的关键参考信息
2.异常可以作为方法内部的一种特殊返回值，以便通知调用者底层的执行情况
 */

import java.text.ParseException;
import java.util.Scanner;

public class exception {
    public static void main(String[] args) throws ParseException {
       //对于编译阶段异常的检测，需要在可能出现异常方法后加上throws 异常类名字
       // 如上面的main方法，对这个main方法如果有ParseException异常就会提醒

       //运行时异常
       //int[] arr = {1,2,3};
       //System.out.println(arr[5]);//ArrayIndexOutOfBoundsException
       // 控制台输出红色的异常信息并且程序终止

       /*异常的三种处理方式
        1.JVM默认处理方式
        2.自己处理
        3.抛出异常
        */
        /*
        JVM默认处理方式
        1.把异常的名称，原因以及出现的位置等信息输出在了控制台
        2.程序停止执行(后面代码不再执行)

        System.out.println("开始");
        System.out.println(1/0);//算术异常 ArithmeticException
        System.out.println("结束");//不会执行
        */
        /*
        自己处理
        目的：当代码出现异常时候，程序可以继续执行
        语法格式：
        try{
            可能出现异常的代码
        } catch(异常类名 变量名){
            异常的处理代码;
        }

        对于try里面的代码变量都是局部变量，出了try就不能再用了(跟局部代码块一样)
         */
        System.out.println("----------------TryCatch使用----------------------");
        int[] arr = {1, 2, 3};
        try {
            System.out.println(arr[5]);/*
       此处会出现异常，程序就会创建一个ArrayIndexOutOfBoundsException类型的对象
       把这个对象交给catch里面小括号里面的变量名来比较，看是否可以接受这个对象
       如果可以接受，就会跳转到执行catch里面的代码,try里面的后面的代码就不会执行了
       catch里面的代码执行完毕后，程序继续执行

       如果try里面的代码没有出现异常，try里面的代码全部执行完毕后，程序继续执行
        */
            System.out.println("继续执行");//不会执行

        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组越界了");
        } catch (NullPointerException | ArithmeticException e) {
            //如果出现A异常或者B异常都采用同一种处理方式，那么可以用|连接
            System.out.println("空指针异常或者算术异常");
        } catch (Exception e) {
            //如果要捕获多个可能出现的异常，这些异常存在继承关系，那么父类异常要放在最下面
            System.out.println("出现了异常");
        }

        System.out.println("继续执行");//会执行
       /*小细节：
       try里面没有异常，try里面的代码全部执行完毕后，程序继续执行
       try可能出现多个异常，就写多个catch
       try的异常没有被catch捕获，就会交给JVM默认处理
       */


        System.out.println("----------------Throwable类的常用方法----------------------");
        /*
        Throwable类的常用方法
        String getMessage() 返回此 throwable 的简短描述。
        String toString() 返回此 throwable 的详细消息字符串。
        void printStackTrace() 把异常的错误信息输出在控制台但不会停止程序运行
        通常用第三个方法，因为前两个方法不够详细
         */
        try {
            System.out.println(arr[100]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.getMessage());//Index 100 out of bounds for length 3
            System.out.println(e.toString());//java.lang.ArrayIndexOutOfBoundsException: Index 100 out of bounds for length 3
            e.printStackTrace();//控制台输出红色的异常信息提醒
        }
        /*小知识：
        err与out的区别
        System.out.println("正常输出");
        System.err.println("用来打印错误信息的，在控制台输出的红色的信息");
        printStackTrace()底层调用的就是err的println方法
        */


        System.out.println("-----------------throw关键字使用---------------------");
       //throw关键字使用
        int[] arr1 = null;//或者int [] arr1 ={};或者 int [] arr1 = new int[0];
        int maxn=0;
        try {
            maxn= getMax(arr1);//这个方法在下面
        } catch (NullPointerException e) {
          System.out.println("空指针异常");
        } finally {
            //finally里面的代码不管有没有异常都会执行
            //跟写在try—catch体系后面本质一样的
            //但finally一般用来释放资源,比如关闭文件，关闭数据库连接等
        }
        System.out.println(maxn); // 0


        System.out.println("-----------------异常应用案例---------------------");
        //一个异常应用案例
        Scanner  cin = new Scanner(System.in);
        GirlFriend gf = new GirlFriend();
        System.out.println("请输入女朋友的年龄：");
        while(true){
            try {
                int age = cin.nextInt();
                gf.setAge(age);

                System.out.println("年龄设置成功");
                break;//如果没有异常，就跳出循环

            } catch (AgeOutOfBoundsException e) {
                System.out.print(e.getMessage());
                // System.out.print("年龄超出范围，请重新输入:");
            }
        }

    }//main方法结束

    public  static int  getMax(int [] a) throws ParseException, NullPointerException{
        //throws写在方法定义出，表示本方法可能会出现异常，交给调用者处理
        //可能出现多个异常，用逗号隔开
        //大部分情况，编译有问题也编译器会提醒，没必要再用throws输出到控制台了

        //写在方法体中，结束方法，手动抛出异常，交给调用者处理，后面的代码不会执行
        //定义里面是shrows 方法里面是throw哦
        if(a==null) throw new NullPointerException();
        //C++中 throw可以抛出自定义类型，但Java必须是异常类(Throwable类体系)

        int max=a[0];
        for (int i = 0; i < a.length; i++) if(a[i]>max) max=a[i];
        return max;
    }

}//public class exception 结束


/*
自定义异常类
1.定义一个类继承Exception或者RuntimeException
2.提供两个构造方法，一个无参构造，一个带参构造
3.把父类的构造方法用super()调用
意义：就是让控制台的报错信息更加见名知意
类名格式：最后都加Exception
*/
class AgeOutOfBoundsException extends RuntimeException{
    public AgeOutOfBoundsException() {}
    /*带参构造，可以在创建对象的时候，把自己定义异常信息传递进去
     然后可以通过getMessage()方法获取到这个自己想输出的异常信息*/
    public AgeOutOfBoundsException(String message) {
        super(message);
    }
}

class GirlFriend{
  private  int age;

    public void setAge(int age) {
        if(age<18||age>40)
            throw new AgeOutOfBoundsException("年龄超出范围，请重新输入：");
        //throw new AgeOutOfBoundsException();
        /*对于自定义异常，如果不使用带参构造
        那么getMessage()方法返回的是  null
            toString()方法返回的是异常类的名字 Exception.AgeOutOfBoundsException
            printStackTrace() 控制台不会有任何提醒
         */
        this.age = age;
    }


}